﻿#################################
# BASIC Tenliners Contest 2018  #
#                               #
# Golf64                        #
# (c) 2018 Roman Werner @romwer #
# email: roman.werner@gmail.com #
#################################

Golf64 is a cute little golf simulation with realistic trajectory and gravity physics.
Play a random (or specific*) 9-hole golf course and find out your handicap. Then invite your friends to see if they can beat it.

*If you want to visit a specific golf course then before you «RUN» the game, you need to apply «C=RND(-n)» where n is the course 1-9 you want to play.


Task and Goal
=============
- Try to get the ball into the hole with minimum number of strikes. The less strikes it takes, the better your handicap.
- The final handicap you get after playing a full course. A course in this game consists of 9 holes.
- Each hole has a predefined par, which is the average number of strikes a semi-professional player needs to finish the hole.
- Your handicap starts at 0. If you need more (or less) strikes than what the par number says, then the difference will be added to your handicap.
- For each hole your caddy chooses the "best" iron for you so you will need to adjust your power to the different angle.
- If you finish a 9-hole course with a handicap of 0 or higher then you can call yourself a semi-pro or pro golfer. But it takes a lot of practice to achieve this.
- Now good luck and have a successful round!


Control:
========
<space bar> on keyboard -or- fire button* (Joystick port 1)
Keep pressing and watch the POWER go up, then release to strike. Note that if you keep pressing after POWER 127 the number will restart at 0.

* This game is compatible with the official SiBUGA (Single Button Game) controller - let me know if you like one :)


Status Display:
===============
ANG  = The angle the golf ball will take off (depends of the iron the caddy gives you. The iron and therefore angle changes with every hole)
PWR  = The power (or strength) you hit the ball with
HCP  = Your handicap, starting with 0
HOLE = The hole you currently play (1-9) and next to it the distance from the teeing green to the hole in meters
PAR  = The predefined par and next to it the number of attempts you had so far


Game Requirements:
==================
- C64/C128* with BASIC 2.0 runtime environment (or compatible)
- 1 Player

*Also playable on Windows/MacOS/iOS/Android (C64 Emulator required)


Starting in VICE: C64 emulator (http://vice-emu.sourceforge.net/):
==================================================================
Start the emulator and then drag and drop the "golf64.prg" into the VICE window.
-or-
Load the program with the command: load "golf64.prg",8
Then start the program with:       run


Golf64 BASIC program code:
==========================
0m=53280:pOm,6:pOm+1,14:?"{light green}{clear}":e=0:h=h+1:c=c+p-q:q=0:z=20:k=z:j=197:h$="{home}{down}{white} hcp:"
1a=int(rN(1)*25+20):g=int(rN(1)*60)+h*30:p=int(g/150)+3:v$="{home}{down}{down}{right}{right}{right}{right}    {left}{left}{left}{left}":f=0
2goS9:?:fOi=1to40:?"{reverse on}{light green} {down}{left} {down}{left} {up}{up}{reverse off}";:pOs+i,160:nE:pOo,190:ifh>9tH?h$c:wAj,u:cL:gO0
3?"{home}{down}{blue} ang"atA27)"hole"h"{left} "g"{left}m":?" pwr ?  "tA28)"par"p;q:?"{down} hcp"c:t=.1:wAj,60
4v=(v+1)aN127:?v$v;:on-(pE(j)<>64)gO4:q=q+1:?tA34)q:goS9:r=aB(a+180*f)*{pi}/180
5x=v*cos(r)*t:y=v*sI(r)*t-5*t^2-4.9*t^2:z=20-(y/2*aB(y>0)):e=d+x:f=e>g:w=190+f*2
6t=t+0.1:?tA20)"      {home}";:on-(z>=0)goS9:pO1944-k,160:ifn<19aNn>-22tHpO1885+n,160
7n=int(g-e):k=e+20-int((e+20)/40)*40:ifn<19aNn>-22tHpO1885+n,32:w=160:u=60
8pO1944-k,w:on-(y>0)gO5:v=0:d=e:on-(n<>-1)gO3:?" {down}{left}{white}Q":pOm,5:wAj,60:d=0:gO0
9pO214,z:pO211,20:sY58640:?"{white}Q{yellow}"int(e)"{left}m":?"{up}"tA20);:o=1924:s=1983:reT

Tip: Copy/paste the program-Code into CBM prg Studio (http://www.ajordison.co.uk/).
There you can see the commands in nice color highlighting and you can directly execute the game in an emulator of your choice.


Golf64 Tenliner explained (000=row 0/100=row 1/...):
====================================================
000 m=53280: rem base address for border/background-color
001 pokem,6: rem set border color to blue
002 pokem+1,14: rem set background color to light blue
003 print"{light green}{clear}": rem clear screen and fill video ram with light green
004 e=0: rem set ball distance in meters to 0 (teeing ground)
005 h=h+1: rem next hole
006 c=c+p-q: rem calculate handicap by adding par and subtracting used strikes
007 q=0: rem reset number of attempts per hole
008 z=20: rem initial row number to display golf ball
009 k=z: rem assign relative x position (0-39) of angle bracket, initially this is 20
010 j=197: rem use j as a fix value for 197 (address containing the last pressed key)
011 h$="{home}{down}{white} hcp:": rem fix string value for final handicap result

100 a=int(rnd(1)*25+20): rem assign angle (random or based on initial seed)
101 g=int(rnd(1)*60)+h*30: rem assign total distance to hole (random or based on initial seed)
102 p=int(g/150)+3: rem assign par (allowed attempts per hole)
103 v$="{home}{down}{down}{right}{right}{right}{right}    {left}{left}{left}{left}"
104 f=0: rem flag to record whether ball is left or right from hole (0 or -1)

200 gosub900: rem print golf ball at position z (initially at row 20)
201 print: rem place cursor at beginning of next line
202 fori=1to40: rem for 40 columns print 3 lines of green grass
203 print"{reverse on}{light green} {down}{left} {down}{left} {up}{up}{reverse off}";
204 pokes+i,160: rem fill 4th line with reversed space. This to avoid rolling screen.
205 next
206 pokeo,190: rem show inversed angle bracket symbol ">" at screen ram position 1924
207 ifh>9thenprinth$c:waitj,u:clr:goto000: rem check if all 9 holes have been played

300 print"{home}{down}{blue} ang"atab(27)"hole"h"{left} "g"{left}m": rem display status
301 print" pwr ?  "tab(28)"par"p;q: rem display status (second line)
302 print"{down} hcp"c: rem display handicap
303 t=.1: rem initialise time value (a little bit above 0)
304 waitj,60: rem wait until <space> is pressed

400 v=(v+1)and127: rem increase power and when over 127 start again at 0
401 printv$v;: rem place cursor (v$) and print current power value
402 on-(peek(j)<>64)goto400: rem repeat until no key is pressed (64=no key)
403 q=q+1: rem increase counter for number of attempts
404 printtab(34)q: rem print number of attempts
405 gosub900: rem  print golf ball at position z (only to place cursor)
406 r=abs(a+180*f)*{pi}/180: rem convert angle to radian

500 x=v*cos(r)*t: rem x position = power * cos( angle ) * time
501 y=v*sin(r)*t-5*t^2-4.9*t^2: rem y position = power * sin( angle ) * time - gravity
502 z=20-(y/2*abs(y>0)): rem convert y position into position for screen row
503 e=d+x: rem total distance in meters = last distance + distance since last strike
504 f=e>g: rem false=-1=hole is right from ball / true=0=hole is left from ball 
505 w=190+f*2: rem use math to define poke value for inverse ">" or "<"

600 t=t+0.1: rem increase time value by a tenth of a second
601 printtab(20)"      {home}";: rem clear golf ball at last position
602 on-(z>=0)gosub900: rem print golfball only if still visible on screen
603 poke1944-k,160: rem clear the angle bracket at the last relative X position
604 ifn<19andn>-22thenpoke1885+n,160: rem fill hole when it was in reach

700 n=int(g-e): rem difference between total distance - ball distance
701 k=e+20-int((e+20)/40)*40: rem relative x position (0-39) of angle bracket
702 ifn<19andn>-22thenpoke1885+n,32:w=160:u=60: rem if hole is in reach show hole but no angle bracket

800 poke1944-k,w: rem show angle bracket to visualize momentum of speed
801 on-(y>0)goto500: rem repeat as long as ball is still in the air 
802 v=0: rem reset power to prepare for next strike
803 d=e: rem replace old last distance to new last distance
804 on-(n<>-1)goto300: rem prepare next strike as long as ball is not in hole
805 print" {down}{left}{white}Q": rem show ball in hole
806 pokem,5: rem turn border to green
807 waitj,60: rem wait for <space> bar
808 d=0: rem set last distance to 0 since we start again at teeing ground
809 goto000: rem continue with next hole

900 poke214,z: rem set row position
901 poke211,20: rem set column position
902 sys58640: rem place cursor
903 print"{white}Q{yellow}"int(e)"{left}m": rem print ball and distance
904 print"{up}"tab(20);: rem position cursor as preparation for clearing (line 601)
905 o=1924: rem initial screen ram position to show angle bracket character
906 s=1983: rem beginning of lowest screen row to poke grass elements
907 return


Variables used in the code
==========================
a = angle (between 20 and 45 degree)
c = handicap
d = distance from last strike (in relation to teeing ground)
e = ball distance in meters (in relation to teeing ground)
f = flag to check whether ball is left or right from hole (0 or -1)
g = total distance from teeing ground to hole
h = hole (1-9)
h$ = "{home}{down}{white} HCP:" (used to print final handicap result)
i = only used in the for/next loop
j = 197 (zeropage address containing the last pressed key)
k = relative x position (0-39) of angle bracket related to the distance e
m = 53280 (base address for border/background color)
n = difference between total distance and ball distance
o = 1924 (initial screen ram position to show angle bracket character)
p = par number (allowed attempts per hole without making minus points)
q = current number of strikes/attempts used per hole
r = angle converted to radion
s = beginning of lowest screen row to poke grass elements
t = time value (as part of trajectory formula)
u = 60 (peek value for <space> key)
v = power for shot / speed
v$ = String to position cursor in order to display the increasing power value
w = poke value for inverse ">" (190) or "<" (188)
x = current x position in meters for current shot (as part of trajectory formula)
y = current y position in meters for current shot (as part of trajectory formula)
z = relative value of y for row cursor position before printing golf ball on screen


Trivia
======
- In golf the angle of a clubhead is called "Loft". A specific club is called "Chipper" => "LoftChipper" => "LiftChopper" => "ChoppLifter" Haha.
- To avoid "IF" statements a number of tricks were used, e.g. the following code...
  if e >g then w=188: rem 188 = poke value for inverse "<"
  if e<=g then w=190: rem 190 = poke value for inverse ">"
  ...could be simply replaced with the following statement using a mix of math and true(0)/false(-1) results...
  w=190+(e>g)*2
- The use of the angle brackets ">" and "<" was an elegant design choice to
  1. ...show the direction to the hole
  2. ...indicate which direction the ball will fly to with next strike
  3. ...provide feedback for speed and angle of the flying ball by moving its position in relation to the distance covered
- The angle brackets are being hidden when the hole is coming into reach to "simulate" the putting green
- I learned many new things about golf I didn't know before :)


Tools Used
==========
- VICE 2.4 (http://vice-emu.sourceforge.net/)
- CBM prg Studio V3.11.0 by Arthur Jordison (http://www.ajordison.co.uk)

  Note: CBM prg Studio has a setting to show the number of effective characters used up (great to check if I am still within the PUR-80 limit).
  Unfortunately this is not always correct in version V3.11.0. I thought I was finished but then discovered I was still way over the 80 chars.
  Seems to have something to do when using screen codes. I reported this now to Arthur Jordison, creator of the awesome CBM PRG Studio.


References
==========
This source from Gerhard Rath (in german) helped me to easily understand and adapt the physics formula: http://www.brgkepler.at/~rath/simulation/exc_wurf.html

